package model;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Vector;
import java.util.regex.*;
import java.lang.Math;

public class game {
	private Vector<cage> play = new Vector();
	private String playName,difficulty,dimension;
	private int maxCellValue,minCellValue;
	private FileWriter fstream;
	BufferedWriter out;
	public game() throws IOException{
		fstream = new FileWriter("output.txt");
		out = new BufferedWriter(fstream);

	}
	//LOAD GAME AFTER SCANNING
	public void loadGame(String fileName) throws IOException,NullPointerException{
		String stringToken;
		int count=1;
		Scanner scanner = new Scanner(new FileInputStream(fileName));
		try
		{
			while(scanner.hasNextLine()){
				
				if (count==1){
					stringToken = scanner.nextLine();
					playName = stringToken;
					//count+=1;
				}
				if (count==2){
					stringToken = scanner.nextLine();
					dimension = stringToken;
					maxCellValue = Integer.parseInt(dimension);
					minCellValue = 1;
					
				}
				if (count>=3) {
					stringToken = scanner.nextLine();
					String delimiter = " ";
					int index = stringToken.indexOf("end");
					if (index!=-1)
						break;
					String[] temp = stringToken.split(delimiter);
					play.add(new cage(temp,temp.length));
						
					}
				count=count+1;
			}
		//System.out.println("Count value is "+count);
		}finally {
			scanner.close();
		}
		out.write("File loaded successfully!!\n");
		//printSolution();
	}
	public void validateGame(String fileName) throws IOException, NullPointerException{//use regex and validate the syntax of the input file
		Pattern p1 = Pattern.compile("\\w+");
		Pattern p2 = Pattern.compile("^4");
		Pattern p4 = Pattern.compile("end");
		Pattern p3 = Pattern.compile("\\d{1,2}\\s(\\+|=|\\*|-|/)\\s\\d{1,2}(\\s[ABCD][1-4])+");
		String stringToken;
		int count=0;
		boolean syntaxError = false;
		if (new File(fileName).isFile()==false) {
			out.write("Invalid input filename!! Try again!\n");
			System.exit(0);
		}
		Scanner scanner = new Scanner(new FileInputStream(fileName));
		try{
			while (scanner.hasNextLine() && (syntaxError == false)){
				stringToken = scanner.nextLine();
				count+=1;
				if (count==1){
					Matcher m = p1.matcher(stringToken);
					if (m.find()==false)
						syntaxError=true;
				}
				if (count==2) {
					Matcher m = p2.matcher(stringToken);
					if (m.find()==false)
						syntaxError=true;
				}
				if (count>=3)
				{
					Matcher m = p3.matcher(stringToken);
					Matcher m1 = p4.matcher(stringToken);
					if ((m.find()==false) && (m1.find()==false))
						syntaxError=true;
				}
					
				
			}
				
		}finally{
			scanner.close();
		}
		if (syntaxError==true){
			out.write("Syntax error in the input file!!\n");
			System.exit(0);
		}
		else
			out.write("Input file does not have any syntax errors!!\n");
	}
	public void gameSolutions() throws NoSuchElementException, IOException{
		int count = 0;
		for (int i=0;i<play.size();i++) {
			if (play.elementAt(i).returnsizeOfCage()==1){
				play.elementAt(i).setCellValueWhenCageSizeIsOne();
				play.elementAt(i).setRulesSatifiedForCells();
			}
		}
		while ((!SolutionReached()) && (count<1000)){
		for (int j=1;j<=maxCellValue;j++)// brute force check
			for (int k=1;k<=maxCellValue;k++) {
				if (returnValueOfCell(j,k)==0){
					fillValueForCell(j, k);//fill values for cells
				}
				else {
					if (!checkValidityOfCell(j, k)){
						fillValueForCell(j, k);
					}
				}
			}
		count+=1;
		}
		if (SolutionReached())
			out.write("The game has a unique solution\n");
		else
			out.write("The game does not have a unique solution. Layout could be wrong\n");
	}
	//ADDITION
	public void fillCellsWhenOpIsAdd(int cageNo,int cellRow,int cellColumn) {
		int x=0,y=0;//assuming that a cage has maximum of 2 cells TODO
		int valuesSet=0;
		int currentValue1 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell1(), play.elementAt(cageNo).returnColumnNumberOfCell1());
		int currentValue2 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell2(), play.elementAt(cageNo).returnColumnNumberOfCell2());
		for (x=1;x<=maxCellValue;x++){
			for(y=1;y<=maxCellValue;y++){
				if(x + y==play.elementAt(cageNo).returnTargetNumber()){
					boolean b1 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
					boolean b2 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
					boolean b3 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
					boolean b4 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
					if((x!=currentValue1) && (y!=currentValue2)){
						if ((b1==false) && (b2==false)) {
							play.elementAt(cageNo).setCellValue(x, y);
							play.elementAt(cageNo).setRulesSatifiedForCells();
							valuesSet=1;
							break;
						}
						else if ((b3==false) && (b4==false)){
							play.elementAt(cageNo).setCellValue(y, x);
							play.elementAt(cageNo).setRulesSatifiedForCells();
							valuesSet=1;
							break;
						}
	
					}
										
					
				}
			}
			if (valuesSet==1)
				break;
			}
		if ((valuesSet==0) && (currentValue1==0) && (currentValue2==0)){
			backTracking(cageNo);
		}
				
	}
	
//SUBTRACTION	
public void fillCellsWhenOpIsSub(int cageNo,int cellRow,int cellColumn) {
	int x=0,y=0;//assuming that a cage has maximum of 2 cells TODO
	int valuesSet=0;
	int currentValue1 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell1(), play.elementAt(cageNo).returnColumnNumberOfCell1());
	int currentValue2 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell2(), play.elementAt(cageNo).returnColumnNumberOfCell2());
	for (x=1;x<=maxCellValue;x++){
		for(y=1;y<=maxCellValue;y++){
			if(Math.abs(x - y)==play.elementAt(cageNo).returnTargetNumber()){
				boolean b1 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
				boolean b2 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
				boolean b3 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
				boolean b4 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
				if ((x!=currentValue1) && (y!=currentValue2))
				if ((b1==false) && (b2==false) ) {
					play.elementAt(cageNo).setCellValue(x, y);
					play.elementAt(cageNo).setRulesSatifiedForCells();
					valuesSet=1;
					break;
				}
				else if ((b3==false) && (b4==false)){
					play.elementAt(cageNo).setCellValue(y, x);
					play.elementAt(cageNo).setRulesSatifiedForCells();
					valuesSet=1;
					break;
				}
				
				
			}
		}
		if (valuesSet==1)
			break;
		}
	if ((valuesSet==0) && (currentValue1==0) && (currentValue2==0)){
		backTracking(cageNo);
	}
		
	}
//MULTIPLICATION
public void fillCellsWhenOpIsMul(int cageNo,int cellRow,int cellColumn) {
	int x=0,y=0;//assuming that a cage has maximum of 2 cells TODO
	int valuesSet=0;
	int currentValue1 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell1(), play.elementAt(cageNo).returnColumnNumberOfCell1());
	int currentValue2 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell2(), play.elementAt(cageNo).returnColumnNumberOfCell2());
	for (x=1;x<=maxCellValue;x++){
		for(y=1;y<=maxCellValue;y++){
			if(x*y==play.elementAt(cageNo).returnTargetNumber()){
				boolean b1 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
				boolean b2 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
				boolean b3 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
				boolean b4 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
				if ((x!=currentValue1) && (y!=currentValue2)){
					if ((b1==false) && (b2==false)) {
						play.elementAt(cageNo).setCellValue(x, y);
						play.elementAt(cageNo).setRulesSatifiedForCells();
						valuesSet=1;
						break;
					}
					else if ((b3==false) && (b4==false)){
						play.elementAt(cageNo).setCellValue(y, x);
						play.elementAt(cageNo).setRulesSatifiedForCells();
						valuesSet=1;
						break;
					}
				}
				
				
				
			}
		}
		if (valuesSet==1)
			break;
		}
	if ((valuesSet==0) && (currentValue1==0) && (currentValue2==0)){
		backTracking(cageNo);
	}
	
}
//DIVISION
public void fillCellsWhenOpIsDiv(int cageNo,int cellRow,int cellColumn) {
	int x=0,y=0;//assuming that a cage has maximum of 2 cells TODO
	int valuesSet=0;
	int currentValue1 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell1(), play.elementAt(cageNo).returnColumnNumberOfCell1());
	int currentValue2 = returnValueOfCell(play.elementAt(cageNo).returnRowNumberOfCell2(), play.elementAt(cageNo).returnColumnNumberOfCell2());
	for (x=1;x<=maxCellValue;x++){
		for(y=1;y<=maxCellValue;y++){
			if(x/y==play.elementAt(cageNo).returnTargetNumber()){
				boolean b1 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
				boolean b2 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
				boolean b3 = checkForValueInRowColumn(x,play.elementAt(cageNo).returnRowNumberOfCell2(),play.elementAt(cageNo).returnColumnNumberOfCell2());
				boolean b4 = checkForValueInRowColumn(y,play.elementAt(cageNo).returnRowNumberOfCell1(),play.elementAt(cageNo).returnColumnNumberOfCell1());
				if ((x!=currentValue1) && (y!=currentValue2) ){
					if ((b1==false) && (b2==false)) {
						play.elementAt(cageNo).setCellValue(x, y);
						play.elementAt(cageNo).setRulesSatifiedForCells();
						valuesSet=1;
						break;
					}
					else if ((b3==false) && (b4==false)){
						play.elementAt(cageNo).setCellValue(y, x);
						play.elementAt(cageNo).setRulesSatifiedForCells();
						valuesSet=1;
						break;
					}	
				}
				
				
				
			}
		}
		if (valuesSet==1)
			break;
		}
	if ((valuesSet==0) && (currentValue1==0) && (currentValue2==0)){
		backTracking(cageNo);
	}
}

public int returnValueOfCell(int row,int column){
	for(int j=0;j<play.size();j++){
		for(int i=0;i<play.elementAt(j).listOfCells.size();i++) {
			if (play.elementAt(j).listOfCells.elementAt(i).findCell(row, column))
				return play.elementAt(j).listOfCells.elementAt(i).returnBlockNumber();
		}
	}
	return 0;
}
public void fillValueForCell(int row,int column){//TODO break from loop once value is found
	int solved=0;
	for(int i=0;i<play.size();i++){
		for(int j=0;j<play.elementAt(i).listOfCells.size();j++) {
			if (play.elementAt(i).listOfCells.elementAt(j).findCell(row, column)){
				if(play.elementAt(i).returnOperator().equals("+")){
					fillCellsWhenOpIsAdd(i,row,column);
					solved=1;
					break;
				}
				if(play.elementAt(i).returnOperator().equals("-")){
					fillCellsWhenOpIsSub(i,row,column);
					solved=1;
					break;
				}
				if(play.elementAt(i).returnOperator().equals("*")){
					fillCellsWhenOpIsMul(i,row,column);
					solved=1;
					break;
				}
				if(play.elementAt(i).returnOperator().equals("/")){
					fillCellsWhenOpIsDiv(i,row,column);
					solved=1;
					break;
				}
			}
				
			}
		if (solved==1)
			break;
		}
	}
public boolean checkForValueInRowColumn(int cellValue, int row, int column){
for(int i=0;i<play.size();i++){
		if(play.elementAt(i).checkCellValueInRowColumn(row, column, cellValue)==true){
			return true;
		}
	}
	return false;
}

//RETURNS THE VALIDITY OF THE CELL (tempRow,tempColumn)
public boolean checkValidityOfCell(int tempRow,int tempColumn){
	for(int i=0;i<play.size();i++){
		for(int j=0;j<play.elementAt(i).listOfCells.size();j++){
		if (play.elementAt(i).listOfCells.elementAt(j).findCell(tempRow, tempColumn))
				return play.elementAt(i).listOfCells.elementAt(j).returnValidity();
			}
		}
	return false;
}
public boolean SolutionReached(){
	for(int i=0;i<play.size();i++){
		for(int j=0;j<play.elementAt(i).listOfCells.size();j++){
			if(!play.elementAt(i).listOfCells.elementAt(j).returnValidity())
				return false;
		}
		
	}
	return true;
}

//PRINTS THE ENTIRE GAME!
public void printSolution() throws IOException{
	for(int i=0;i<play.size();i++){
		out.write("Cage "+i);
		out.write("Target value: "+play.elementAt(i).returnTargetNumber());
		out.write("Cell Info is \n"+play.elementAt(i).returnCellInfo());
	}
		
}
public void closeOutStream() throws IOException{
	out.close();
}
public void backTracking(int cageNo){
	int row1 = play.elementAt(cageNo).returnRowNumberOfCell1();
	int row2 = play.elementAt(cageNo).returnRowNumberOfCell2();
	int col1 = play.elementAt(cageNo).returnColumnNumberOfCell1();
	int col2 = play.elementAt(cageNo).returnColumnNumberOfCell2();
	
	for(int i=1;i<=maxCellValue;i++){
		if ((i!=col1) && (i!=col2)) {
			if (returnValueOfCell(row1, i)!=0)
				fillValueForCell(row1, i);
		}
			
	}
	for(int i=1;i<=maxCellValue;i++){
		if ((i!=col2) && (i!=col1)) {
			if (returnValueOfCell(row2, i)!=0)
				fillValueForCell(row2, i);
		}
			
	}
	for(int i=1;i<=maxCellValue;i++){
		if ((i!=row1) && (i!=row2)){
			if (returnValueOfCell(i, col1)!=0)
			fillValueForCell(i, col1);
		}
			
	}
	for(int i=1;i<=maxCellValue;i++){
		if ((i!=row2) && (i!=row1)){
			if (returnValueOfCell(i, col2)!=0)
			fillValueForCell(i, col2);
		}
			
	}
		
		
			
}

//END OF CODE
}


